/*
 * Copyright (c) 2003, 2004, 2005, 2006 Israfil Consulting Services Corporation
 * Copyright (c) 2003, 2004, 2005, 2006 Christian Edward Gruber
 * All Rights Reserved
 * 
 * This software is licensed under the Berkeley Standard Distribution license,
 * (BSD license), as defined below:
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this 
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of Israfil Consulting Services nor the names of its contributors 
 *    may be used to endorse or promote products derived from this software without 
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 * 
 * $Id: MockResultSetMetaData.java 27 2006-02-13 19:40:43Z cgruber $
 */
package net.israfil.foundation.mock.sql;

import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * An implementation of DatabaseMetaData that partners with ResultSet to 
 * provide database-disconnected fixed results for testing via the JDBC 
 * APIs.
 * 
 * @author <a href="mailto:cgruber@israfil.net">Christian Edward Gruber </a>
 */
public class MockResultSetMetaData implements ResultSetMetaData {
	
	public static int ZERO_ORDER_LIST_OFFSET = 1;
	List columns;
	
	MockResultSetMetaData(String[] columns) {
		if (columns == null) throw new IllegalArgumentException("Cannot construct a MockResultSet with no columns.");
		ColSpec[] colspecs = new ColSpec[columns.length];
		for (int i = 0 ; i < columns.length ; i++) {
			colspecs[i] = new ColSpec(columns[i]);
		}
		prepColumns(colspecs);
	}
	
	MockResultSetMetaData(ColSpec[] columns) { 
		prepColumns(columns);
	}
	
	private void prepColumns(ColSpec[] columns) {
		if (columns == null || columns.length <= 0) 
			throw new IllegalArgumentException("Cannot construct a MockResultSet with no columns.");
		this.columns = new ArrayList(Arrays.asList(columns)); 
	}
	
	public void finalize() throws Throwable {
		columns = null;
	}
	
	/**
	 * Given a column name, this method returns the number of the column, or 0 if 
	 * no such column exists.
	 * 
	 * @param name
	 * @return
	 */
	public int getColumnNumber(String name) {
		for (int i = 1; i <= columns.size(); i++)
			if (((ColSpec)columns.get(i-ZERO_ORDER_LIST_OFFSET)).name.equalsIgnoreCase(name)) return i;
		return 0;
	}
	
	public String getCatalogName(int column) throws SQLException {
		return "";
	}

	public String getColumnClassName(int column) throws SQLException {
		throw new UnsupportedOperationException("Not yet implemented.");
	}

	public int getColumnCount() throws SQLException {
		return columns.size();
	}

	public int getColumnDisplaySize(int column) throws SQLException {
		return ((ColSpec)columns.get(column-ZERO_ORDER_LIST_OFFSET)).width;
	}

	public String getColumnLabel(int column) throws SQLException {
		if (column < 1) throw new IllegalArgumentException("Column number must be greater than 0.");
		return ((ColSpec)columns.get(column-ZERO_ORDER_LIST_OFFSET)).name;
	}

	public String getColumnName(int column) throws SQLException {
		if (column < 1) throw new IllegalArgumentException("Column number must be greater than 0.");
		return ((ColSpec)columns.get(column-ZERO_ORDER_LIST_OFFSET)).name;
	}

	public int getColumnType(int column) throws SQLException {
		return ((ColSpec)columns.get(column-ZERO_ORDER_LIST_OFFSET)).type.sqltype;
	}

	public String getColumnTypeName(int column) throws SQLException {
			return ((ColSpec)columns.get(column-ZERO_ORDER_LIST_OFFSET)).type.name;
	}

	public int getPrecision(int column) throws SQLException {
		return ((ColSpec)columns.get(column-ZERO_ORDER_LIST_OFFSET)).precision;
	}

	public int getScale(int column) throws SQLException {
		return ((ColSpec)columns.get(column-ZERO_ORDER_LIST_OFFSET)).scale;
	}

	public String getSchemaName(int column) throws SQLException {
		return "";
	}

	public String getTableName(int column) throws SQLException {
		return "";
	}

	public boolean isAutoIncrement(int column) throws SQLException {
		return false;
	}

	public boolean isCaseSensitive(int column) throws SQLException {
		return false;
	}

	public boolean isCurrency(int column) throws SQLException {
		return false;
	}

	public boolean isDefinitelyWritable(int column) throws SQLException {
		throw new UnsupportedOperationException("Not yet implemented.");
	}

	public int isNullable(int column) throws SQLException {
		return ((ColSpec)columns.get(column-ZERO_ORDER_LIST_OFFSET)).nullable ? ResultSetMetaData.columnNullable : ResultSetMetaData.columnNoNulls;
	}

	public boolean isReadOnly(int column) throws SQLException {
		return true;
	}

	public boolean isSearchable(int column) throws SQLException {
		throw new UnsupportedOperationException("Not yet implemented.");
	}

	public boolean isSigned(int column) throws SQLException {
		return false;
	}

	public boolean isWritable(int column) throws SQLException {
		throw new UnsupportedOperationException("Not yet implemented.");
	}
	
	public static class ColSpec {
		public final String name;
		public final DBType type;
		public final int width;
		public final int precision;
		public final int scale;
		public final boolean nullable;
		
		public ColSpec(String name) {
			this.name = name;
			this.type = DBType.types[DBType.OBJECT];
			this.width = 0;
			this.precision = 0;
			this.scale = 0;
			this.nullable = true;
		}
		public ColSpec(String name, boolean nullable) {
			this.name = name;
			this.type = DBType.types[DBType.OBJECT];
			this.width = 0;
			this.precision = 0;
			this.scale = 0;
			this.nullable = nullable;
		}
		public ColSpec(String name, DBType type) {
			this.name = name;
			this.type = type;
			this.width = 0;
			this.precision = 0;
			this.scale = 0;
			this.nullable = true;
		}
		public ColSpec(String name, DBType type, boolean nullable) {
			this.name = name;
			this.type = type;
			this.width = 0;
			this.precision = 0;
			this.scale = 0;
			this.nullable = nullable;
		}
		public ColSpec(String name, DBType type, int width) {
			this.name = name;
			this.type = type;
			this.width = width;
			this.precision = 0;
			this.scale = 0;
			this.nullable = true;
		}
		public ColSpec(String name, DBType type, int precision, int scale) {
			this.name = name;
			this.type = type;
			this.precision = precision;
			this.scale = scale;
			this.width = 0;
			this.nullable = true;
		}
		public ColSpec(String name, DBType type, int width, boolean nullable) {
			this.name = name;
			this.type = type;
			this.width = width;
			this.precision = 0;
			this.scale = 0;
			this.nullable = nullable;
		}
		public ColSpec(String name, DBType type, int precision, int scale, boolean nullable) {
			this.name = name;
			this.type = type;
			this.precision = precision;
			this.scale = scale;
			this.width = 0;
			this.nullable = nullable;
		}
	}

}
